package com.ss.summer.sql.config;


import com.ss.summer.sql.annotations.STable;
import com.ss.summer.sql.core.CustomSql;
import com.ss.summer.sql.statics.CustomSqlMap;
import com.ss.summer.utlis.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.JarEntry;

/**
 * 扫描标注了 @STable的类 ,在静态类中添加一个sql
 * @author xia17
 * @date 2019/7/25 14:48
 */
@Component
@EnableConfigurationProperties({SummerProperties.class})
@Slf4j
public class ApplyScanAutoConfig  {

    private SummerProperties summerProperties;
    @Autowired
    public ApplyScanAutoConfig(SummerProperties summerProperties){
        this.summerProperties = summerProperties;
    }
    private String classPath = this.getClass().getResource("/").getPath();
    private List<String> basePacks = null;
    private Set<String> classPaths = null;
    private static final String ENDS_WITH = ".class";

    /**
     * Bean 初始化完成后执行该方法
     */
    @PostConstruct
    public void init() {
        initBasePacks();
    }

    /**
     * 初始化
     */
    private void initBasePacks(){
        //判断是否是测试运行 既@Test运行
        final String testClassName = "test-classes";
        if (classPath.contains(testClassName)){
            classPath =  classPath.substring(0, classPath.length() - testClassName.length()-1);
            classPath += "classes/";
        }
        //获取包入径
        String packages = summerProperties.getApplyPackages();
        if (packages == null || "".equals(packages)){
            log.info("未配置申请实体类所在的位置");
            return ;
        }
        basePacks = StringUtil.cutStringByChar(packages, ',');
        //开始扫描类
        scanPackages();
        //扫描jar包
        try {
            //默认值false
            Boolean needScanJar = summerProperties.getNeedScanJar();
            if (needScanJar == null){
                needScanJar = false;
            }
            if (needScanJar){
                scanJars();
                log.info("开始扫描jar包里的申请类");
            }
        } catch (IOException e) {
            log.error("在扫描jar包里的申请类时发生文件读取异常");
            e.printStackTrace();
        }
        try {
            loaderClass();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 读取jar 包里的文件
     * @throws IOException 读取文件异常
     */
    public void  scanJars() throws IOException {
        Enumeration<URL> resources;
        URL url;
        JarEntry jarEntry;
        //循环配置的basePacks
        for (String basePack : basePacks) {
            //获取包含该包的入径
            resources = Thread.currentThread().getContextClassLoader().getResources(basePack.replace('.', '/'));
            while (resources.hasMoreElements()){
                url = resources.nextElement();
                //如果是jar包继续执行
                if ("jar".equals(url.getProtocol())){
                    JarURLConnection connection = (JarURLConnection) url.openConnection();
                    //获取该jar包的每个文件
                    Enumeration<JarEntry> entries = connection.getJarFile().entries();
                    while (entries.hasMoreElements()){
                         jarEntry = entries.nextElement();
                         //如果该文件是class文件,且包含basePack 则加到需加载的文件中去
                         if (!jarEntry.isDirectory() && jarEntry.getName().endsWith(ENDS_WITH) && jarEntry.getName().contains(basePack.replace('.', '/'))){
                             this.addClass(jarEntry.getName());
                         }
                    }
                }
            }
        }
    }


    /**
     * 扫描
     */
    private void scanPackages(){
        String searchPath;
        String basePackPath;
        //扫描包下的类
        for (String basePack : basePacks) {
            log.info("开始扫描"+basePack);
            basePackPath = basePack.replace('.',File.separatorChar);
            searchPath = classPath + basePackPath;
            File file = new File(searchPath);
            if (!file.exists()){
                log.error("未找到包" + basePack);
            }
            if (!file.isDirectory()){
                log.error(basePack + "不是一个文件夹");
            }
            scanDir(file);
        }
    }

    /**
     * 扫描具体的一个包，递归扫描包下所有的文件及子包
     * @param file 文件
     */
    private void scanDir(File file){
        if (file.isDirectory()){
            File[] files = file.listFiles();
            if (files == null){
                return;
            }
            for (File sunFile : files) {
                scanDir(sunFile);
            }
        }else {
            if (file.getName().endsWith(ENDS_WITH)){
                addClass(file.getPath());
            }
        }
    }

    /**
     * 如果是Class文件则将入径加入 classPaths Set集合中
     * @param filePath 文件入径
     */
    private void addClass(String filePath){
        if (classPaths == null){
            classPaths = new HashSet<>();
            classPaths.add(filePath);
        }else {
            classPaths.add(filePath);
        }
    }

    /**
     * 读取Class文件
     * @throws ClassNotFoundException 类未找到异常
     */
    private void loaderClass() throws ClassNotFoundException {
        if (classPaths == null){
            return;
        }
        //通过反射读取Class文件
        for (String classPath : classPaths) {
            classPath = classPath.replace(this.classPath.replace("/","\\").
                    replaceFirst("\\\\",""),"").replace("\\",".").replace(".class","");
            Class cls = Class.forName(classPath);
            addCustomSqlMap(cls);
        }
    }

    /**
     * 加入静态类中
     * @param clz 类型类
     */
    private void addCustomSqlMap(Class clz){
        if (clz.isAnnotationPresent(STable.class)){
            CustomSqlMap.put(clz.getName(),new CustomSql(clz));
        }
    }


}
